#include "event/fd_event.hpp"

// 不要放在头文件fd_event.hpp中, 因为只是实现依赖loop.hpp中的定义
// 依赖关系是 fd_event.hpp <- loop.hpp <- fd_event.cpp
#include "event/loop.hpp"

using namespace ibox::event;

EpollFdEvent::EpollFdEvent(EpollLoop *loop,
                           int fd,
                           uint32_t events,
                           Event::Mode mode)
    : m_loop{loop}, m_fd{fd}, m_events{events}, m_mode{mode} {
    unref_fd_shared_data();

    if (m_mode == FdEvent::Mode::Oneshot) {
        m_is_stop_after_trigger = true;
    }

    m_data = m_loop->query_fd_shared_data(m_fd);
    if (m_data == nullptr) {
        m_data = std::make_shared<EpollFdSharedData>();
        assert(m_data != nullptr);

        memset(&m_data->ev, 0, sizeof(m_data->ev));
        m_data->ev.data.ptr = static_cast<void *>(m_data.get());

        m_loop->add_fd_shared_data(m_fd, m_data);
    }

    ++m_data->ref;
}

void EpollFdEvent::unref_fd_shared_data() {
    if (m_data) {
        m_data->ref--;
        if (m_data->ref == 0) {
            m_loop->remove_fd_shared_data(m_fd);
            m_data = nullptr;
            m_fd = -1;
        }
    }
}

bool EpollFdEvent::enable() {
    if (m_data == nullptr) {
        return false;
    }
    if (m_is_enabled) {
        return true;
    }
    if (static_cast<bool>(m_events & FdEvent::ReadEvent)) {
        m_data->read_events.push_back(this);
    }
    if (static_cast<bool>(m_events & FdEvent::WriteEvent)) {
        m_data->write_events.push_back(this);
    }

    reload_epoll();

    m_is_enabled = true;
    return true;
}

bool EpollFdEvent::disable() {
    if (m_data == nullptr || !m_is_enabled) {
        return true;
    }
    if (static_cast<bool>(m_events & FdEvent::ReadEvent)) {
        auto iter = std::find(
            m_data->read_events.begin(), m_data->read_events.end(), this);
        m_data->read_events.erase(iter);
    }

    if (static_cast<bool>(m_events & FdEvent::WriteEvent)) {
        auto iter = std::find(
            m_data->write_events.begin(), m_data->write_events.end(), this);
        m_data->write_events.erase(iter);
    }

    reload_epoll();

    m_is_enabled = false;
    return true;
}

void EpollFdEvent::on_event_callback([[maybe_unused]] int fd,
                                     uint32_t events,
                                     void *obj) {
    auto *data = static_cast<EpollFdSharedData *>(obj);
    if (static_cast<bool>(events & EPOLLIN)) {
        for (EpollFdEvent *event : data->read_events) {
            event->on_event(FdEvent::ReadEvent);
        }
    }

    if (static_cast<bool>(events & EPOLLOUT)) {
        for (EpollFdEvent *event : data->write_events) {
            event->on_event(FdEvent::WriteEvent);
        }
    }
}

void EpollFdEvent::on_event(uint32_t event) {
    if (m_is_stop_after_trigger) {
        disable();
    }

    m_loop->before_handle_one_event();
    if (m_cb) {
        m_cb(event);
    }
    m_loop->after_handle_one_event();
}

// 重新加载fd对应的epoll
void EpollFdEvent::reload_epoll() {
    uint32_t old_events = m_data->ev.events;
    uint32_t new_events = 0;

    if (!m_data->write_events.empty()) {
        new_events |= EPOLLOUT;
    }
    if (!m_data->read_events.empty()) {
        new_events |= EPOLLIN;
    }

    m_data->ev.events = new_events;

    if (old_events == 0) {
        if (new_events != 0) {
            epoll_ctl(m_loop->epoll_fd(), EPOLL_CTL_ADD, m_fd, &m_data->ev);
        }
    } else {
        if (new_events != 0) {
            epoll_ctl(m_loop->epoll_fd(), EPOLL_CTL_MOD, m_fd, &m_data->ev);
        } else {
            epoll_ctl(m_loop->epoll_fd(), EPOLL_CTL_DEL, m_fd, nullptr);
        }
    }
}
